package liverdungeon.actors;

//  An Actor is something that can change in the game.

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import liverdungeon.*;
import liverdungeon.net.*;
//  Each actor has a position in the world and an idNum
//  that represents who they are.

public abstract class Actor implements Serializable 
{
    public static final byte DIRECTION_NORTH = 0;
    public static final byte DIRECTION_EAST = 1;
    public static final byte DIRECTION_SOUTH = 2;
    public static final byte DIRECTION_WEST = 3;
    
    Position position = new Position(0,0);   //  See "Position" class
    byte idNum, health, direction;    //  A byte representing how many hits until death occurs.
    private transient BufferedImage image;  //  Every actor has a picture... right?
    ArrayList<Location> visibleLocs, path, accessibleLocs, bestPath = null;
    
    public Actor(Position p, byte id, byte h)
    {
        position = p;
        idNum = id;
        health = h;
        direction = DIRECTION_SOUTH;
        loadImage();
    }
    
    public Position getPosition()   //  Returns the Actors Position.
    {
        return position;
    }
    
    public void setPosition(Position p)   //  Sets Position with a Position object.
    {
        position = p;
    }
    
    public void setPosition(byte x_pos, byte y_pos)     //  Sets Position with an X and Y coordinate
    {
        position = new Position(x_pos, y_pos);
    }
    
    public byte getID()     //  Returns the idNum of the Actor
    {
        return idNum;
    }
    
    public void setID(byte id)   //    Sets idNum to the next available value;
    {
        idNum = id;
    }
    
    public byte getHealth()     //  Returns the health of the Actor
    {
        return health;
    }
    
    public void setHealth(byte h)   //    Sets health to the the specified byte value;
    {
        health = h;
    }
    
    public Location getLocation()
    {
        byte x = (byte) ((position.getX()+(Global.SQUARE_SIZE/2))/Global.SQUARE_SIZE);
        byte y = (byte) ((position.getY()+(Global.SQUARE_SIZE/2))/Global.SQUARE_SIZE);
        return new Location(x,y);
    }
    
    public Location getRearCornerLocation()
    {
        byte x,y;
        
        if(direction==DIRECTION_EAST || direction==DIRECTION_SOUTH)
        {
            x = (byte) (position.getX()/25);
            y = (byte) (position.getY()/25);
        }
        else
        {
            x = (byte) ((position.getX()+25)/25);
            y = (byte) ((position.getY()+25)/25);
        }
        return new Location(x,y);
    }
    
    public void move()
    {
            if(direction == DIRECTION_NORTH)
                this.setPosition((byte)(getPosition().getX()), (byte)(getPosition().getY()-5));
            if(direction == DIRECTION_EAST)
                this.setPosition((byte)(getPosition().getX()+5), (byte)(getPosition().getY()));
            if(direction == DIRECTION_SOUTH)
                this.setPosition((byte)(getPosition().getX()), (byte)(getPosition().getY()+5));
            if(direction == DIRECTION_WEST)
                this.setPosition((byte)(getPosition().getX()-5), (byte)(getPosition().getY()));
    }
    
    public byte getDirection()
    {
        return direction;
    }
    
    public void setDirection(byte d)
    {
        direction = d;
    }
    
    public byte getDirectionToward(Actor a)
    {
        if(position.getX() > a.position.getX())
            return DIRECTION_NORTH;
        else if(position.getX() < a.position.getX())
            return DIRECTION_SOUTH;
        else 
        {
            if(position.getY()>a.position.getY())
                return DIRECTION_WEST;
            else return DIRECTION_EAST;
        }
    }
    
    public BufferedImage getImage()
    {
        return image;
    }
    public void setImage(BufferedImage img)
    {
        image = img;
    }
    public abstract void loadImage();
    public ActorPackage createPackage()
    {
        ActorPackage packet = new ActorPackage();
        packet.id = getID();
        packet.position = getPosition();
        packet.direction = direction;
        //test
        return packet;
    }
    
    public void die()
    {
        Global.level.remove(this);
    }
    
    public Actor getIntersectingActor()
    {
        ArrayList<Location> locs = getLocation().getAdjacentLocations();
        locs.add(getLocation());
        
        for(Location loc : locs)
        {
            Actor a = Global.level.getActorAt(loc);
            
            if(a!=null)
            {
                if(Math.abs(a.position.getX()-position.getX())<25 && Math.abs(a.position.getY()-position.getY())<25)
                return a;
            }
        }
        
        return null;
    }
    
    public ArrayList<Location> getPath(Location loc, Location end)
    {        
        path.add(loc);
        
        if (loc.equals(end))
        {
            if(bestPath == null || bestPath.size()>path.size())
                bestPath = path;
            path.remove(loc);
            return path;
        }
        
        for(byte i = 0; i < 4; i++)
        {
            Location currentLoc = loc.getLocationIn(i);
            
            if(accessibleLocs.contains(currentLoc) && !(path.contains(currentLoc)))                       //  If it is accessible
            {
                path.addAll(getPath(currentLoc,end));
                return path;
            }
        }        

        path.remove(loc);
        return path;
    }
    
    public void turn()
    {
        int cnt = 0;
        int random = (int) (Math.random()*2);
        Actor a = Global.level.getActorAt(getLocation().getLocationIn(direction));
        
        while(cnt==0 || (a != null && !(a instanceof Hero) && cnt<4))
        {
            if(random==0)
            {
                direction++;
                if(direction>3) direction = 0;
            }
            else
            {
                direction--;
                if(direction<0) direction = 3;
            }
            
            a = Global.level.getActorAt(getLocation().getLocationIn(direction));
            cnt++;
        }

    }
    
    public abstract void loseHealth();
    public abstract void act();
}
